package com.uc3m.dsic.gestorZoo;

import java.sql.*;
import java.util.Collection;
import java.util.ArrayList;

/**
 * Clase que gestiona las distintas tablas pertenecientes a un Zoo en una base
 * de datos Microsoft Access.
 *
 * @author Guillermo Solano Méndez
 * @version 1.0
 */
public class GestorZooAplicacion implements GestorZooInterface {

    /**
     * Constante que define el nombre de la clase que gestion la base de datos
     */
    final static String NOMBRE_CLASE_BD = "sun.jdbc.odbc.JdbcOdbcDriver";
    /**
     * Constante con el driver de la base de datos access
     */
    final static String DRIVER_BD = "jdbc:odbc:Driver={Microsoft Access"
            + " Driver (*.mdb)};DBQ=./";
    /**
     * Constante con el Nombre del fichero access donde se almacenan las tablas
     */
    final static String FICHERO_BD = "Zoo.mdb";
    /**
     * Constante array de nombres de las distintas tablas dentro del archivo
     * access (definida por la constante anterior FICHEROS_BD. Estas tablas
     * hacen un mapeado uno a uno con el enumerador Tablas. Más info en el 
     * comentario del enum Tablas.
     */
    final static String[] TABLAS_BD = {"ZOO", "SALA", "ANIMAL", "HORARIO",
        "CUIDA", "CUIDADOR", "TICKET"};
    /**
     * Constante que define el comienzo de la consulta SELECT ALL FROM.
     */
    final static String SELECT_ALL = "SELECT * FROM ";

    /**
     * Enumerador que define las distintas tablas que se
     * encuentran en la base de datos. Realiza un mapeado entre el enumerador
     * y los nombres de las tablas de la base de datos.
     * Para obtener el nombre de la tabla llamar al método 
     * String getNombreTabla(Enum Tablas) con la tabla que se desea.
     */
    public enum Tablas {

        ZOO, TICKET, SALA, ANIMAL, CUIDADOR, CUIDA, HORARIO
    };
    /**
     * Conexion con las distintas tablas de la base de datos.
     */
    private Connection conexion;

    /**
     * Constructor único de la clase GestorZooAplicacion. Crea la conexion con
     * la base de datos y  abre el socket de comunicación.
     */
    public GestorZooAplicacion() {
        // existe la clase del la base de datos
        try {
            Class.forName(NOMBRE_CLASE_BD);
        } catch (java.lang.ClassNotFoundException e) {
            //e.printStackTrace();
        }
        try {
            // se abren las conexiones con los distintos ficheros
            conexion = DriverManager.getConnection(DRIVER_BD + FICHERO_BD,
                    "", "");

        } catch (SQLException e) {
            //System.err.println("Error: " + e.getLocalizedMessage());
        }
    }

    /**
     * Actualización (consulta, inserción o borrado) de la base de datos.
     *
     * @param consulta Consulta de la base de datos, del estilo
     *              "INSERT INTO Sala VALUES"
     * @param param   Los distintos parametros pasados por array. Por ejemplo
     *             {"20", "200", "sala de los monos", "id_zoo"}. El método ya
     *             se encarga de insertarlo con el formato correcto
     *             ('20','200','sala de los monos','id_zoo').
     * @return     o bien la primera fila de la consulta o 0 si la consulta no
     *             devolvió nada.
     * @exception SQLException si se produce un error en la base de datos
     */
    public int actualiza(String consulta,
            String[] param) throws SQLException {

        StringBuffer sb = new StringBuffer("(");

        for (int i = 0; i < param.length - 1; i++) {
            sb.append("'" + param[i].trim() + "',");
        }
        sb.append("'" + param[param.length - 1].trim() + "')");
        
        return this.actualiza(consulta + sb.toString());
    }

    /**
     * Actualización (consulta, inserción o borrado) de la base de datos.
     *
     * @param consulta Consulta de la base de datos completa. Por ejemplo:
     *             "DELETE INTO Zoo WHERE id_zoo='id_zoo_buscado'.
     * @return     o bien la primera fila de la consulta o 0 si la consulta no
     *             devolvió nada.
     * @exception SQLException si se produce un error en la base de datos
     */
    public int actualiza(String consulta) throws SQLException {

        Statement sentencia = conexion.createStatement();

        return sentencia.executeUpdate(consulta);
    }

    /**
     * Interfaz ResultSet con toda la tabla solicitada
     *
     * @param  t Enum Tablas con la tabla deseada.
     * @return ResultSet con el set de la tabla
     * @exception SQLException si se produce un error en la base de datos
     */
    /* nota: el tratamiento es externo al método. Y como se realizó para las
     * practicas 1 y 2 */
    public ResultSet getTabla(Tablas t) throws SQLException {

        String tabla = this.getNombreTabla(t);

        if (tabla != null) {
            return this.ejecutarConsulta(SELECT_ALL + tabla);
        } else {
            return null;
        }
    }

    /**
     * Interfaz ResultSet con toda la tabla solicitada
     *
     * @param  tabla con el nombre de tabla deseado
     * @return ResultSet con el set de la tabla
     * @exception SQLException si se produce un error en la base de datos,
     *                         o el nombre de la tabla es incorrecta
     */
    public ResultSet getTabla(String tabla) throws SQLException {

        /*for (int i=0; i<TABLAS_BD.length; i++){
        if (!TABLAS_BD[i].equals(tabla))
        return null;
        }
        return this.ejecutarConsulta(SELECT_ALL + tabla);
         */

        if (tabla != null) {
            return this.ejecutarConsulta(SELECT_ALL + tabla);
        } else {
            return null;
        }

    }

    /**
     * Realiza una consulta a la base de datos
     *
     * @param consulta completa pasada en un String
     * @return ResultSet con el set de la tabla
     * @exception SQLException si se produce un error en la base de datos,
     *                         o la consulta es incorrecta incorrecta
     */
    public ResultSet ejecutarConsulta(String consulta) throws SQLException {

        Statement sentencia = 
                conexion.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                ResultSet.CONCUR_READ_ONLY);

        return sentencia.executeQuery(consulta);

    }

    /**
     * Función de mapeado que dado una tabla definida por el Enum Tablas
     * devuelve el nombre concreto de la tabla.
     *
     * @param  t Enum Tablas con la tabla que se desea obtener
     * @return el nombre de la tabla definida en TABLAS_BD
     *
     */
    protected String getNombreTabla(Tablas t) {

        String idTabla = null;

        switch (t) {
            case ZOO:
                idTabla = TABLAS_BD[0];
                break;
            case SALA:
                idTabla = TABLAS_BD[1];
                break;
            case ANIMAL:
                idTabla = TABLAS_BD[2];
                break;
            case HORARIO:
                idTabla = TABLAS_BD[3];
                break;
            case CUIDA:
                idTabla = TABLAS_BD[4];
                break;
            case CUIDADOR:
                idTabla = TABLAS_BD[5];
                break;
            case TICKET:
                idTabla = TABLAS_BD[6];
                break;
        }
        return idTabla;

    }

    /**
     * Función de mapeado que dado una tabla definida por un String Enum Tablas
     * devuelve el  Enum Tablas unico de la tabla. De esta manera se obtiene el
     * enum de la tabla a partir de su nombre.
     *
     * @param  nombreTabla con la tabla que se desea obtener
     * @return Tablas enum de la tabla definida en TABLAS_BD
     *
     */
    protected Tablas getEnumTabla(String nombreTabla) {

        Tablas idTabla = null;

        if (nombreTabla.trim().equals(TABLAS_BD[0])) {
            idTabla = Tablas.ZOO;
        } else if (nombreTabla.trim().equals(TABLAS_BD[1])) {
            idTabla = Tablas.SALA;
        } else if (nombreTabla.trim().equals(TABLAS_BD[2])) {
            idTabla = Tablas.ANIMAL;
        } else if (nombreTabla.trim().equals(TABLAS_BD[3])) {
            idTabla = Tablas.HORARIO;
        } else if (nombreTabla.trim().equals(TABLAS_BD[4])) {
            idTabla = Tablas.CUIDA;
        } else if (nombreTabla.trim().equals(TABLAS_BD[5])) {
            idTabla = Tablas.CUIDADOR;
        } else if (nombreTabla.trim().equals(TABLAS_BD[6])) {
            idTabla = Tablas.TICKET;
        }

        return idTabla;
    }

    /**
     * Cierra la base de datos y la almacena en disco
     */
    public void cerrarBD() {

        try {
            this.conexion.close();

        } catch (SQLException e) {
            System.err.println("Error: " + e.getLocalizedMessage());
        }
    }

    /**
     * @return devuelve un array con todos los nombres de la base de datos
     */
    protected String[] nombresTablas() {
        String r[] = new String[TABLAS_BD.length];

        for (int i = 0; i < TABLAS_BD.length; i++) {
            r[i] = new String(TABLAS_BD[i]);
        }
        return r;
    }

    /**
     * Genera una estructura de datos con toda las filas contenidas en la
     * tabla especificada por parametro.
     * @param nombreTabla nombre de la tabla definido por el enumerador
     *         GestorZooAplicacion.Tablas
     * @return  La primera coleccion hace referencia a la fila de la tabla,
     *          la segunda a la columna actual.
     * @throws ZooException Si no el nombre introducido por parametro no
     *          coincide con el nombre de una tabla de la base de datos zoo.
     * @throws SQLException  Si se produce un error con la consulta SQL
     */
    /*NOTA: los nombres de las tablas vienen definidas en la constante
    GestorZooAplicacion.TABLAS_BD*/
    public Collection<Collection<String>> getEDTabla(Tablas nombreTabla)
            throws ZooException, SQLException {

        Collection<Collection<String>> ed = new ArrayList<Collection<String>>();
        Collection<String> temp;
        ResultSetMetaData meta;
        int cont;
        int max;

        ResultSet rs = this.getTabla(nombreTabla);
        if (rs == null) {
            throw new ZooException("No se puede generar la ED porque " 
                    + "el ResultSet es igual a null. El nombre de tabla no"
                    + " debe existir. Inserte un nombre de los designados"
                    + " en GestorZoo.TABLAS_BD");
        }
        /* acceso a los metadatos de la tabla*/
        meta = rs.getMetaData();
        /* numero máx de columnas de la tabla */
        max = meta.getColumnCount();
        /* la primera fila incluye las etiquetas con los nombres
        de las columnas*/
        temp = new ArrayList<String>();
        for (int i = 1; i <= max; i++) {
            temp.add(meta.getColumnLabel(i));
        }
        /* se inserta la primera fila con el nombre de columnas para imprimir*/
        ed.add(temp);
        /*y por cada fila de la tabla */
        while (rs.next()) {
            temp = new ArrayList<String>(); // nueva fila
            cont = 1; //primera columna
            while (cont <= max) {
                temp.add(rs.getString(cont++));//columna actual
            }
            ed.add(temp);// anyade fila
        }

        return ed;
    }

    // ---- PRUEBAS GESTOR ----- //
    /*
    public static void main(String arg[]) throws Exception {

        // tratar de usar la interfaz...
        GestorZooInterface prueba = new GestorZooAplicacion();

        //OPCION 1: tratamiento a traves de ResultSet
        // un ResultSet por cada tabla y se compara entre ellos para conseguir
        // la consulta deseada
        ResultSet rs = prueba.getTabla(GestorZooAplicacion.Tablas.ZOO);
        //Los enum de la tabla vienen definidas en el enum Tablas
        //o bien se puede obtener la tabla...
        rs = prueba.getTabla("ZOO");
        // los nombres de las tablas vienen definidas por el array de Strings
         // TABLAS_BD[] (constante) de la clase GestorZooAplicacion.
         // Si el nombre no se encuentra en la constante TABLAS_BD[] devolverá
         // null
         //
        int cont;
        // nota: para saber el número de columnas...
        int max = rs.getMetaData().getColumnCount();

        while (rs.next()) {
            cont = 1; //siempre empieza por 1, primera columna..
            while (cont <= max) {
                System.out.print(rs.getString(cont++) + " -");
            }
            System.out.println("");
        }


    //OPCION 2: Tratamiento a partir de la ED definida por la interfaz
    //Collection http://java.sun.com/javase/6/docs/api/java/util/Collection.html
        Collection<Collection<String>> cole;
        cole = prueba.getEDTabla(GestorZooAplicacion.Tablas.ANIMAL);
        // se me ocurren dos formas distintas de tratarlos...
          // 1ª, con forin.
        for (Collection<String> fila : cole) { //para cada fila de la tabla...
              // nota: la primera fila, primera iteración del forin, devuelve
             // los nombres que definen las columnas de la tabla
            //
            for (String columna : fila) {// para cada atributo de la fila...
                System.out.println(columna + " ");
// la primera iteracion del segundo forin, devolvería
// NOMBRE  ESPECIE   EDAD   DESCRIPCION   PAIS   EXISTINCION   ENFERMEDAD   SALA
// la segunda iteración devolvería la primera fila de la tabla:
// Chin-Lui	Pandas	5	null 	China	NO	ninguna	  nul
///
            }
        }
        //2ª, usando iterator
        java.util.Iterator<String> ite;
        for (Collection<String> fila : cole) { //para cada fila de la tabla...

            ite = fila.iterator();
            while (ite.hasNext()) {
                //acceso a cada elemento. Recordad, la primera fila tiene los
                //nombres de las columnas de la tabla
                System.out.println(ite.next() + " ");
            }
        }
 */
        /*TODOS ESTOS EJEMPLOS RECORRERIAN TODA LA TABLA Y LA MOSTRARIAN
         POR PANTALLA*/



        /*
        // muestra por salida estándar el contenido de todas las tablas
        for (String s: TABLAS_BD){
        System.out.println("TABLA: "+ s + " -----");
        ResultSet t = p.getTabla(s);
        int cont;
        int max = t.getMetaData().getColumnCount(); //saber numero de columnas

        while (t.next()){
        cont = 1;
        while (cont <= max)
        System.out.print(t.getString(cont++) +" -");
        System.out.println("");
        }

        System.out.println("");
        System.out.println("");
        }
         
    }
         */ //FINNN
}
